//
//  KZDatePicker.swift
//  KZSwiftMoudel
//
//  Created by 朱凯 on 2018/6/20.
//  Copyright © 2018年 JooKey. All rights reserved.
//

import UIKit

struct KZDateComponentShow: OptionSet {
    
    let rawValue: UInt
    static let year     = KZDateComponentShow(rawValue: 1)
    static let month    = KZDateComponentShow(rawValue: 1 << 1)
    static let day      = KZDateComponentShow(rawValue: 1 << 2)
    static let hour     = KZDateComponentShow(rawValue: 1 << 3)
    static let minute   = KZDateComponentShow(rawValue: 1 << 4)
    static let second   = KZDateComponentShow(rawValue: 1 << 5)
}

enum KZDateStyle {
    case all                    // 年月日时分秒
    case yearMonthDay           // 年月日
    case hourMinuteSecond       // 时分秒
    case yearMonthDayHourMinute // 年月日时分
}

class KZDatePicker: UIControl {

    // MARK: ------ 外部
    /// 最小的时间，默认是1970/1/1 00:00:00
    var minLimitDate: Date = Date(timeIntervalSince1970: 0) {
        didSet {
            if minLimitDate > maxLimitDate {
                minLimitDate = oldValue
            } else {
                if minLimitDate > scrollToDate {
                    scrollToDate = minLimitDate
                }
            }
        }
    }
    /// 最大的时间，默认是当前时间10年后
    var maxLimitDate: Date = Date(timeIntervalSinceNow: 60 * 60 * 24 * 365 * 10) {
        didSet {
            if maxLimitDate < minLimitDate {
                maxLimitDate = oldValue
            } else {
                if maxLimitDate < scrollToDate {
                    scrollToDate = maxLimitDate
                }
            }
        }
    }
    /// 默认显示的当前时间
    var scrollToDate: Date = Date() {
        didSet {
            if scrollToDate > maxLimitDate {
                scrollToDate = maxLimitDate
            }
            if scrollToDate < minLimitDate {
                scrollToDate = minLimitDate
            }
        }
    }
    
    /// 确定按钮背景色，默认蓝色
    var sureButtonBackgroundColor: UIColor = kBlueColor {
        didSet {
            sureButton.backgroundColor = sureButtonBackgroundColor
        }
    }
    
    /// 确定按钮字体颜色，默认白色
    var sureButtonTitleColor: UIColor = UIColor.white {
        didSet {
            sureButton.setTitleColor(sureButtonTitleColor, for: .normal)
        }
    }
    
    /// 年 背景水印，设置无色可以隐藏
    var yearPlaceholderColor: UIColor = UIColor.init(white: 0.95, alpha: 1) {
        didSet {
            yearLabel.textColor = yearPlaceholderColor
        }
    }
    
    // MARK: ------ 私有
    private let bottomView: UIView          = UIView()
    private let sureButton: UIButton        = UIButton()
    private let yearLabel: UILabel          = UILabel()
    private let pickerView: UIPickerView    = UIPickerView()
    
    private var componentsArray: Array<Dictionary> = [Dictionary<String, Array<Int>>]()
    
    /// 显示的时间样式
    private var style: KZDateStyle = .all
    
    // 键名
    private let yearKey     = "年"
    private let monthKey    = "月"
    private let dayKey      = "日"
    private let hourKey     = "时"
    private let minuteKey   = "分"
    private let secondKey   = "秒"
    
    // 回调
    private var completionHandler: (_ date: Date?) -> Void = {_ in }
    
    deinit {
        print("日期选择器销毁")
    }
    
    ///
    /// - Parameters:
    ///   - style: 类型
    ///   - scrollToDate: 滚动到的时间，如果不设刚是当前时间
    convenience init(style: KZDateStyle = .all, scrollToDate: Date = Date(), completionHandler: @escaping (_ date: Date?) -> Void) {
        
        self.init(frame: .zero)
        
        self.style = style
        self.scrollToDate = scrollToDate
        self.completionHandler = completionHandler
        
        self.setupUI()
        self.setupData()
    }
    
    static func datePicker(style: KZDateStyle = .all, scrollToDate: Date = Date(), completionHandler: @escaping (_ date: Date?) -> Void) -> KZDatePicker {
        
        return KZDatePicker(style: style, scrollToDate: scrollToDate, completionHandler: completionHandler)
    }
}

// MARK: ------ 事件
extension KZDatePicker {
    
    func show() {
        
        // 自身的点击dis事件
        self.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
        
        // 把自身添加到window上
        kWindow.addSubview(self)
        self.backgroundColor = UIColor.black.withAlphaComponent(0)
        self.snp.makeConstraints { (m) in
            m.edges.equalTo(kWindow)
        }
        kWindow.layoutIfNeeded()
        
        // 动画
        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0, options: [], animations: {
            
            self.backgroundColor = UIColor.black.withAlphaComponent(0.4)
            
            self.bottomView.snp.remakeConstraints { (m) in
                m.left.equalTo(10)
                m.right.equalTo(-10)
                m.bottom.equalTo(self.snp.bottom).offset(-10)
                m.height.equalTo(216 + 40)
            }
            self.layoutIfNeeded()
        }) { (fin) in
            if fin {
                self.scrollTo(self.scrollToDate)
            }
        }
    }
    
    // 消失
    @objc func dismiss() {
        
        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: [], animations: {
            
            self.backgroundColor = UIColor.black.withAlphaComponent(0)
            
            self.bottomView.snp.remakeConstraints { (m) in
                m.left.equalTo(10)
                m.right.equalTo(-10)
                m.top.equalTo(self.snp.bottom)
                m.height.equalTo(216 + 40)
            }
            self.layoutIfNeeded()
        }) { (fin) in
            if fin {
                self.removeFromSuperview()
            }
        }
    }
    
    // 点击确认按钮
    @objc private func onSureButtonAction(_ sender: Any) {
        
        completionHandler(currentDate())
        dismiss()
    }
}

// MARK: ------ 创建视图
extension KZDatePicker {
    
    private func setupUI() {
    
        self.addSubview(bottomView)
        bottomView.backgroundColor = UIColor.white
        bottomView.layer.cornerRadius = 4
        bottomView.layer.masksToBounds = true
        bottomView.snp.makeConstraints { (m) in
            m.left.equalTo(10)
            m.right.equalTo(-10)
            m.top.equalTo(self.snp.bottom)
            m.height.equalTo(216 + 40)
        }
        
        bottomView.addSubview(sureButton)
        sureButton.addTarget(self, action: #selector(onSureButtonAction(_:)), for: .touchUpInside)
        sureButton.backgroundColor = sureButtonBackgroundColor
        sureButton.setTitle("确认", for: .normal)
        sureButton.setTitleColor(sureButtonTitleColor, for: .normal)
        sureButton.snp.makeConstraints { (m) in
            m.left.right.bottom.equalTo(bottomView)
            m.height.equalTo(40)
        }
        
        bottomView.addSubview(pickerView)
        pickerView.dataSource = self
        pickerView.delegate = self
        pickerView.snp.makeConstraints { (m) in
            m.left.right.top.equalToSuperview()
            m.height.equalTo(216)
        }
    
        yearLabel.adjustsFontSizeToFitWidth = true
        yearLabel.numberOfLines = 1
        yearLabel.textColor = yearPlaceholderColor
        yearLabel.textAlignment = .center
        yearLabel.text = String(scrollToDate.year)
        yearLabel.font = UIFont.boldSystemFont(ofSize: 100)
        bottomView.addSubview(yearLabel)
        yearLabel.snp.makeConstraints { (m) in
            m.edges.equalTo(pickerView)
        }
        bottomView.bringSubview(toFront: pickerView)
    }
}

// MARK: ------ 初始化数据
extension KZDatePicker {

    private func setupData() {
        
        componentsArray = [Dictionary]()
        
        switch style {
            
        case .all:
            
            initDataWithAllStyle()
            
        case .yearMonthDay:

            initDataWithYearMonthDayStyle()

        case .hourMinuteSecond:

            initDataWithHourMinuteSecondStyle()

        case .yearMonthDayHourMinute:

            initDataWithYearMonthDayHourMinuteStyle()
        }
    }
    
    private func initDataWithAllStyle() {
        
        componentsArray.append([yearKey : Array(minLimitDate.year...maxLimitDate.year)])
        
        if minLimitDate.haveSameYear(maxLimitDate) {
            componentsArray.append([monthKey : Array(minLimitDate.month...maxLimitDate.month)])
        } else {
            componentsArray.append([monthKey : Array(1...12)])
        }
        
        if minLimitDate.haveSameYearAndMonth(maxLimitDate) {
            componentsArray.append([dayKey : Array(minLimitDate.day...maxLimitDate.day)])
        } else {
            componentsArray.append([dayKey : Array(1...scrollToDate.numberOfDaysInMonth())])
        }
        
        if minLimitDate.haveSameYearMonthAndDay(maxLimitDate) {
            componentsArray.append([hourKey : Array(minLimitDate.day...maxLimitDate.day)])
        } else {
            componentsArray.append([hourKey : Array(0...23)])
        }
        
        if minLimitDate.haveSameYearMonthDayAndHour(maxLimitDate) {
            componentsArray.append([minuteKey : Array(minLimitDate.minute...maxLimitDate.minute)])
        } else {
            componentsArray.append([minuteKey : Array(0...59)])
        }
        
        if minLimitDate.haveSameYearMonthDayHourAndMinute(maxLimitDate) {
            componentsArray.append([secondKey : Array(minLimitDate.minute...maxLimitDate.minute)])
        } else {
            componentsArray.append([secondKey : Array(0...59)])
        }
    }
    
    private func initDataWithYearMonthDayStyle() {
        componentsArray.append([yearKey : Array(minLimitDate.year...maxLimitDate.year)])
        
        if minLimitDate.haveSameYear(maxLimitDate) {
            componentsArray.append([monthKey : Array(minLimitDate.month...maxLimitDate.month)])
        } else {
            componentsArray.append([monthKey : Array(1...12)])
        }
        
        if minLimitDate.haveSameYearAndMonth(maxLimitDate) {
            componentsArray.append([dayKey : Array(minLimitDate.day...maxLimitDate.day)])
        } else {
            componentsArray.append([dayKey : Array(1...scrollToDate.numberOfDaysInMonth())])
        }
    }
    
    private func initDataWithHourMinuteSecondStyle() {
        
        if let date = Date.date("00:00:00", formatter: "HH:mm:ss") {
            minLimitDate = date
        }
        
        if let date = Date.date("23:59:59", formatter: "HH:mm:ss") {
            maxLimitDate = date
        }
        
        componentsArray.append([hourKey : Array(minLimitDate.hour...maxLimitDate.hour)])
        
        if minLimitDate.haveSameYearMonthDayAndHour(maxLimitDate) {
            componentsArray.append([minuteKey : Array(minLimitDate.minute...maxLimitDate.minute)])
        } else {
            componentsArray.append([minuteKey : Array(0...59)])
        }
        
        if minLimitDate.haveSameYearMonthDayHourAndMinute(maxLimitDate) {
            componentsArray.append([secondKey : Array(minLimitDate.second...maxLimitDate.second)])
        } else {
            componentsArray.append([secondKey : Array(0...59)])
        }
    }
    
    private func initDataWithYearMonthDayHourMinuteStyle() {
        componentsArray.append([yearKey : Array(minLimitDate.year...maxLimitDate.year)])
        
        if minLimitDate.haveSameYear(maxLimitDate) {
            componentsArray.append([monthKey : Array(minLimitDate.month...maxLimitDate.month)])
        } else {
            componentsArray.append([monthKey : Array(1...12)])
        }
        
        if minLimitDate.haveSameYearAndMonth(maxLimitDate) {
            componentsArray.append([dayKey : Array(minLimitDate.day...maxLimitDate.day)])
        } else {
            componentsArray.append([dayKey : Array(1...scrollToDate.numberOfDaysInMonth())])
        }
        
        if minLimitDate.haveSameYearMonthAndDay(maxLimitDate) {
            componentsArray.append([hourKey : Array(minLimitDate.day...maxLimitDate.day)])
        } else {
            componentsArray.append([hourKey : Array(0...23)])
        }
        
        if minLimitDate.haveSameYearMonthDayAndHour(maxLimitDate) {
            componentsArray.append([minuteKey : Array(minLimitDate.minute...maxLimitDate.minute)])
        } else {
            componentsArray.append([minuteKey : Array(0...59)])
        }
    }
}

// MARK: ------ 获取当前时间和滑动到某个时间
extension KZDatePicker {
    
    private func currentDate() -> Date? {
        var result = [Int]()
        
        for index in 0..<pickerView.numberOfComponents {
            result.append(componentsArray[index].values.first![pickerView.selectedRow(inComponent: index)])
        }
        
        switch style {
            
        case .all:
            
            guard result.count == 6 else { return nil }
            
            let dateStr = String(result[0]) + "-" + String(result[1]) + "-" + String(result[2]) + " " +
                String(result[3]) + ":" + String(result[4]) + ":" + String(result[5])
            
            return Date.date(dateStr, formatter: "yyyy-MM-dd HH:mm:ss")
            
        case .yearMonthDay:
            
            guard result.count == 3 else { return nil }
            
            let dateStr = String(result[0]) + "-" + String(result[1]) + "-" + String(result[2])
            
            return Date.date(dateStr, formatter: "yyyy-MM-dd")
            
        case .hourMinuteSecond:
            
            guard result.count == 3 else { return nil }
            
            let dateStr = String(result[0]) + ":" + String(result[1]) + ":" + String(result[2])
            
            return Date.date(dateStr, formatter: "HH:mm:ss")
            
        case .yearMonthDayHourMinute:
            
            guard result.count == 5 else { return nil }
            
            let dateStr = String(result[0]) + "-" + String(result[1]) + "-" + String(result[2]) + " " +
                String(result[3]) + ":" + String(result[4])
            
            return Date.date(dateStr, formatter: "yyyy-MM-dd HH:mm")
        }
    }

    private func scrollTo(_ date: Date) {
        
        var scrollToDate = date
        
        if scrollToDate > maxLimitDate {
            scrollToDate = maxLimitDate
        }
        
        if scrollToDate < minLimitDate {
            scrollToDate = minLimitDate
        }
        
        switch style {
            
        case .all:
            
            allStyleScrollTo(scrollToDate)
            
        case .yearMonthDay:
            
            yearMonthDayStyleScrollTo(scrollToDate)
            
        case .hourMinuteSecond:
            
            hourMinuteSecondStyleScrollTo(scrollToDate)
            
        case .yearMonthDayHourMinute:
            
            yearMonthDayHourMinuteStyleScrollTo(scrollToDate)
            
        }
        
    }
    
    private func allStyleScrollTo(_ date: Date) {
        
        guard componentsArray.count == 6 else {
            return
        }
        
        if let yearIndex = componentsArray[0][yearKey]?.index(of: date.year) {
            pickerView.selectRow(yearIndex, inComponent: 0, animated: true)
        }
        
        if let monthIndex = componentsArray[1][monthKey]?.index(of: date.month) {
            pickerView.selectRow(monthIndex, inComponent: 1, animated: true)
        }
        
        if let dayIndex = componentsArray[2][dayKey]?.index(of: date.day) {
            pickerView.selectRow(dayIndex, inComponent: 2, animated: true)
        }
        
        if let hourIndex = componentsArray[3][hourKey]?.index(of: date.hour) {
            pickerView.selectRow(hourIndex, inComponent: 3, animated: true)
        }
        
        if let minuteIndex = componentsArray[4][minuteKey]?.index(of: date.minute) {
            pickerView.selectRow(minuteIndex, inComponent: 4, animated: true)
        }
        
        if let secondIndex = componentsArray[5][secondKey]?.index(of: date.second) {
            pickerView.selectRow(secondIndex, inComponent: 5, animated: true)
        }
    }
    
    private func yearMonthDayStyleScrollTo(_ date: Date) {
        
        guard componentsArray.count == 3 else {
            return
        }
        
        if let yearIndex = componentsArray[0][yearKey]?.index(of: date.year) {
            pickerView.selectRow(yearIndex, inComponent: 0, animated: true)
        }
        
        if let monthIndex = componentsArray[1][monthKey]?.index(of: date.month) {
            pickerView.selectRow(monthIndex, inComponent: 1, animated: true)
        }
        
        if let dayIndex = componentsArray[2][dayKey]?.index(of: date.day) {
            pickerView.selectRow(dayIndex, inComponent: 2, animated: true)
        }
    }
    
    private func hourMinuteSecondStyleScrollTo(_ date: Date) {
        
        guard componentsArray.count == 3 else {
            return
        }
        
        if let hourIndex = componentsArray[0][hourKey]?.index(of: date.hour) {
            pickerView.selectRow(hourIndex, inComponent: 0, animated: true)
        }
        
        if let minuteIndex = componentsArray[1][minuteKey]?.index(of: date.minute) {
            pickerView.selectRow(minuteIndex, inComponent: 1, animated: true)
        }
        
        if let secondIndex = componentsArray[2][secondKey]?.index(of: date.second) {
            pickerView.selectRow(secondIndex, inComponent: 2, animated: true)
        }
    }
    
    private func yearMonthDayHourMinuteStyleScrollTo(_ date: Date) {
        
        guard componentsArray.count == 5 else {
            return
        }
        
        if let yearIndex = componentsArray[0][yearKey]?.index(of: date.year) {
            pickerView.selectRow(yearIndex, inComponent: 0, animated: true)
        }
        
        if let monthIndex = componentsArray[1][monthKey]?.index(of: date.month) {
            pickerView.selectRow(monthIndex, inComponent: 1, animated: true)
        }
        
        if let dayIndex = componentsArray[2][dayKey]?.index(of: date.day) {
            pickerView.selectRow(dayIndex, inComponent: 2, animated: true)
        }
        
        if let hourIndex = componentsArray[3][hourKey]?.index(of: date.hour) {
            pickerView.selectRow(hourIndex, inComponent: 3, animated: true)
        }
        
        if let minuteIndex = componentsArray[4][minuteKey]?.index(of: date.minute) {
            pickerView.selectRow(minuteIndex, inComponent: 4, animated: true)
        }
        
    }
}

// MARK: ------ picker 代理
extension KZDatePicker: UIPickerViewDelegate, UIPickerViewDataSource {
    
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return componentsArray.count
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if let array = componentsArray[component].values.first {
            return array.count
        }
        return 0
    }
    
    func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
        return 40
    }
    
    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
        
        var title: String = " "
        let dic = componentsArray[component]
        if let key = dic.keys.first {
            let data = dic[key]
            title = String(data![row]) + key
        }
        
        if let label = view as? UILabel {
            label.text = title
            return label
        }
        
        let label = UILabel()
        label.textAlignment = .center
        label.font = UIFont.systemFont(ofSize: 17)
        label.text = title
        label.sizeToFit()
        return label
    }
    
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        // 月分变化了，天数要跟着变化.年份变化了，如果是2月,天数也可能变化
        
        func scrollToValidTimeRange() {
            // 检查当前时间是否在最大和最小时间之前
            if let currentDate = currentDate() {
                
                if currentDate > maxLimitDate {
                    // 超过最大值，则滚回最大值
                    scrollTo(maxLimitDate)
                    
                } else if currentDate < minLimitDate {
                    // 小于最小值，刚滚回最小值
                    scrollTo(minLimitDate)
                }
            }
        }
        
        guard let key = componentsArray[component].keys.first, key == yearKey || key == monthKey else {
            // 只是滚动年或者月时，才需要处理天数的变化
            scrollToValidTimeRange()
            
            return
        }
        
        let yearIndex = pickerView.selectedRow(inComponent: 0)
        if yearIndex < 0 {
            scrollToValidTimeRange()
            
            return
        }
        
        if key == yearKey {
            // 更新年份水印
            let year = componentsArray[0][yearKey]![yearIndex]      // 年份
            
            yearLabel.text = String(year)
        }
        
        if key == yearKey && pickerView.selectedRow(inComponent: component + 1) != 1 {
            // 滚动年，月分不是2月时，不需要处理天数变化
            scrollToValidTimeRange()
            
            return
        }
        
        
        let monthIndex = pickerView.selectedRow(inComponent: 1)
        if monthIndex < 0 {
            
            scrollToValidTimeRange()
            
            return
        }
        
        let year = componentsArray[0][yearKey]![yearIndex]      // 年份
        let month = componentsArray[1][monthKey]![monthIndex]   // 月份
        
        guard let date = Date.date(String(year) + "-" + String(month), formatter: "yyyy-MM") else {
            scrollToValidTimeRange()
            
            return
            
        }
        
        let numberOfDays = date.numberOfDaysInMonth() // 当前月的天数
        
        if numberOfDays != 0 && numberOfDays == componentsArray[2][dayKey]!.count {
            // 天数没有变化，不需要任何操作
            scrollToValidTimeRange()
            
            return
        }
        
        let newDayDic = [dayKey : Array(1...numberOfDays)] // 新的天数数据
        
        componentsArray.replaceSubrange(2...2, with: [newDayDic])
        
        pickerView.reloadComponent(2)
        
        scrollToValidTimeRange()
    }

}
